package ferp.core.game;

import ferp.core.card.Card;

/**
 * User: igorgok
 * Date: 5/19/11 5:31 PM
 */
public class Bid
{
  // available bid types
  public enum Type {PASS, HALF_WHIST, WHIST, PLAY, WITHOUT_3, MISERE, CHECK_10}

  // maximum bid
  public static final Bid MAX = Bid.play(Card.Suit.NONE, Game.MAXIMUM_TRICKS);

  public static Bid misere() {return new Bid(Type.MISERE, Card.Suit.NONE, 0, true);}
  public static Bid play(Card.Suit trump, int tricks) {return new Bid(Type.PLAY, trump, tricks, true);}
  public static Bid play() {return play(Card.Suit.NONE, 0);}
  public static Bid without3() {return new Bid(Type.WITHOUT_3, Card.Suit.NONE, 0, true);}
  public static Bid pass() {return new Bid(Type.PASS, Card.Suit.NONE, 0, true);}
  public static Bid whist(int tricks) {return new Bid(Type.WHIST, Card.Suit.NONE, tricks, true);}
  public static Bid halfWhist(int tricks) {return new Bid(Type.HALF_WHIST, Card.Suit.NONE, tricks, true);}
  public static Bid check(int tricks) {return new Bid(Type.CHECK_10, Card.Suit.NONE, tricks, true);}

  public Type type;
  // number of declared tricks
  public int tricks;
  // raw value, plain sum of per suit estimations
  public int t10000;
  // trump
  public Card.Suit trump;
  // with or without talon on misere or 9 (currently not used)
  public boolean talon;

  // no args constructor for GSON
  public Bid() {reset();}
  // copy other bid
  public Bid(Bid other) {assign(other);}

  // playing?
  public boolean isPlay() {return type == Type.PLAY;}
  // playing misere?
  public boolean isMisere() {return type == Type.MISERE;}
  // passing?
  public boolean isPass() {return type == Type.PASS;}
  // half-whisting?
  public boolean isHalfWhist() {return type == Type.HALF_WHIST;}
  // whisting?
  public boolean isWhist() {return type == Type.WHIST;}
  // without 3?
  public boolean isWithout3() {return type == Type.WITHOUT_3;}
  // check 10?
  public boolean isCheck10() {return type == Type.CHECK_10;}
  // stalingrad?
  public boolean isStalingrad(Settings settings)
  {
    return !settings.isRostov() && settings.whistOn6Spades == Settings.WhistOn6Spades.MANDATORY &&
           type == Type.PLAY && tricks == 6 && trump == Card.Suit.SPADES;
  }
  // check 10?
  public boolean check10(Settings settings)
  {
    return tricks == 10 && settings.tricking10 == Settings.Tricking10.CHECKED;
  }

  // reset bid data
  public void reset()
  {
    type = null;
    tricks = 0;
    t10000 = 0;
    trump = Card.Suit.NONE;
    talon = true;
  }

  public Bid assign(Bid other)
  {
    type = other.type;
    tricks = other.tricks;
    t10000 = other.t10000;
    trump = other.trump;
    talon = other.talon;

    return this;
  }

  // comparing - equals
  public boolean equals(Bid other)
  {
    return (this == other) || (type == other.type && tricks == other.tricks && trump == other.trump && talon == other.talon);
  }

  // comparing PASS, PLAY and MISERE bid types
  public boolean greater(Bid other)
  {
    // no other bid case
    if (other == null)
      return true;

    // check pass case
    if (this.isPass())
      return false;
    if (other.isPass())
      return true;

    // check misere case
    if (this.isMisere() && other.isMisere())
      return false;
    if (other.isMisere())
      return other.talon ? this.tricks > 8 : (this.tricks == 9 && !this.talon || this.tricks > 9);
    if (this.isMisere())
      return this.talon ? other.tricks < 9 : other.tricks < 9 && !other.talon;

    // play case
    // with the equal number of tricks the trump wins
    if (this.tricks == other.tricks)
      return this.trump.ordinal() > other.trump.ordinal();

    return this.tricks > other.tricks;
  }

  // get next bid, higher than this one
  public Bid next(Game g)
  {
    // misere can be outbid with 9 + talon game
    if (isMisere())
      return Bid.play(Card.Suit.SPADES, 9);

    // bid "9 + talon" can be outbid with "misere + no talon" on the first round only
//    if (isPlay() && tricks == 9 && talon && g.rounds == 0)
//      return Bid.misere(false);

    // no trump - raise the trick number
    if (trump == Card.Suit.NONE)
    {
      // we've reached the maximum
      if (tricks == 10)
        return null;

      return new Bid(Type.PLAY, Card.Suit.SPADES, tricks + 1, true);
    }

    // otherwise go to the next trump
    return new Bid(Type.PLAY, Card.Suit.next(trump), tricks, true);
  }

  public void adjust(Bid minimum)
  {
    if (!equals(minimum) && !greater(minimum))
    {
      tricks = minimum.tricks;

      if (!equals(minimum) && !greater(minimum) && tricks < 10)
        ++tricks;
    }
  }

/*
  public void increment()
  {
    // no trump - raise the trick number
    if (trump == Card.Suit.NONE)
    {
      // we've reached the maximum
      if (tricks == 10)
        return;

      trump = Card.Suit.SPADES;
      ++tricks;
    }
    else
      // otherwise go to the next trump
      trump = Card.Suit.next(trump);

    talon = true;
  }

  public void decrement()
  {
    // spades - lower the trick number
    if (trump == Card.Suit.SPADES)
    {
      // we've reached the minimum
      if (tricks == 6)
        return;

      trump = Card.Suit.NONE;
      --tricks;
    }
    else
      // otherwise go to the previous trump
      trump = Card.Suit.previous(trump);
  }
*/

  @Override
  public String toString()
  {
    if (type != null)
      switch (type)
      {
        case PASS:       return "pass";
        case HALF_WHIST: return "whist_half";
        case WHIST:      return "whist_full";
        case PLAY:       return "" + tricks + trump;
        case MISERE:     return "misere";
        case WITHOUT_3:  return "without3";
        case CHECK_10:   return "check10";
      }

    return "";
  }

  private Bid(Type type, Card.Suit trump, int tricks, boolean talon)
  {
    this.type = type;
    this.trump = trump;
    this.tricks = tricks;
    this.talon = talon;
  }
}
